//
//  Lattice.cpp
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#include "Lattice.h"
// Site

template<>
int& Site::getTotalBonds<Site::XYBondData>() {
	return XYTotalBonds_;
}
template<>
int& Site::getTotalBondsOther<Site::XYBondData>() {
	return IsingTotalBonds_;
}
template<>
int& Site::getTotalBonds<Site::IsingBondData>() {
	return IsingTotalBonds_;
}
template<>
int& Site::getTotalBondsOther<Site::IsingBondData>() {
	return XYTotalBonds_;
}
template<>
vector<Site::XYBondData>& Site::getBondArray<Site::XYBondData>() {
	return XYBondArray_;
}
template<>
vector<Site::IsingBondData>& Site::getBondArray<Site::IsingBondData>() {
	return IsingBondArray_;
}
//Lattice
void Lattice::initSquare(boost::array<int, DIM> const &latSize) {
	cordinationNumber_ = 2 * DIM;
	siteLattice_.init(latSize);
	XYBondLattice_.init(latSize);
	IsingBondLattice_.init(latSize);
	latSize_ = latSize;
	SiteIterIndex iter = siteLattice_.beginIndex();
	for (; iter != siteLattice_.endIndex(); iter++) {
		IndexType currentPos = iter.getIndex();
		//iter->siteIndex_ = currentPos;
		XYBondLattice_(currentPos).resize(DIM);
		IsingBondLattice_(currentPos).resize(DIM);
		iter->XYTotalBonds_ = iter->IsingTotalBonds_ = 0;
		for (int d = 0; d < DIM; d++) {
			// XY Bonds
			XYBondType& currentbondXY = XYBondLattice_(currentPos)[d];
			currentbondXY.bondVal[LEFT] = currentbondXY.bondVal[RIGHT] = 0;
			//currentbondXY.dir = d;
			// Ising Bonds
			IsingBondType & currentbondIsing = IsingBondLattice_(currentPos)[d];
			currentbondIsing.bondVal = 0;
			//currentbondIsing.dir = d;
			// Update NN
			boost::array<int, DIM> nPos = currentPos;
			if (nPos[d] == (latSize_[d] - 1)) {
				nPos[d] = 0;
			} else
				nPos[d]++;
			SiteIter nIter = siteLattice_.iterByPos(nPos);
			// XY Bonds
			iter->XYBondArray_.push_back(
					XYBondData(&currentbondXY, nIter, RIGHT, CREATE));
			iter->XYBondArray_.push_back(
					XYBondData(&currentbondXY, nIter, LEFT, REMOVE));
			nIter->XYBondArray_.push_back(
					XYBondData(&currentbondXY, iter.getIter(), LEFT, CREATE));
			nIter->XYBondArray_.push_back(
					XYBondData(&currentbondXY, iter.getIter(), RIGHT, REMOVE));
			// Ising Bonds
			iter->IsingBondArray_.push_back(
					IsingBondData(&currentbondIsing, nIter, CREATE));
			iter->IsingBondArray_.push_back(
					IsingBondData(&currentbondIsing, nIter, REMOVE));
			nIter->IsingBondArray_.push_back(
					IsingBondData(&currentbondIsing, iter.getIter(), CREATE));
			nIter->IsingBondArray_.push_back(
					IsingBondData(&currentbondIsing, iter.getIter(), REMOVE));
		}
	}
//    for (; iter != siteLattice_.endIndex(); iter++) {
//        std::sort(iter->bondArray_.begin(), iter->bondArray_.end(),
//                  [](BondData bond1,BondData bond2)->bool
//        {
//            if (bond1.dir>bond2.dir) return true;
//            else
//                if (bond.moveDir==LEFT return true
//                    else return false;
//        })
//    }
}

void Lattice::init(boost::array<int, DIM> const &latSize) {
	this->initSquare(latSize);
	ira_ = siteLattice_.begin();
	masha_ = siteLattice_.begin();
//	ira_->totalBonds_ = 1;
//	string outPath = "/home/snirgaz";
//	this->dump(outPath);
//	this->read(outPath);
}

bool Lattice::isClosed() const {
	return ira_ == masha_;
}
Lattice::SiteIter Lattice::getMasha() {
	return masha_;
}
Lattice::SiteIter Lattice::getIra() {
	return ira_;
}
void Lattice::setMasha(Lattice::SiteIter &iter) {
	masha_ = iter;
}
void Lattice::setIra(Lattice::SiteIter &iter) {
	ira_ = iter;
}
int Lattice::getCordinationNumber() const {
	return cordinationNumber_;
}

int Lattice::getNumOfSites() const {
	return siteLattice_.totalSize();
}

Lattice::SiteIter Lattice::getSite(int distanceFromStart) {
	return siteLattice_.begin() + distanceFromStart;
}
boost::array<int, DIM> Lattice::getSizes() {
	return latSize_;
}
int Lattice::getTotalBonds() const {
	return totalBonds_;
}
void Lattice::setToalBonds(int totalBonds) {
	totalBonds_ = totalBonds;
}
bool Lattice::testConfiguration(int &totalBonds) {
	totalBonds = 0;
	SiteIter it = siteLattice_.begin();
	for (; it != siteLattice_.end(); it++) {
		int count = 0;
		Site::XYBondArray::iterator itbXY = it->XYBondArray_.begin();
		for (; itbXY != it->XYBondArray_.end(); itbXY++) {
			if (itbXY->createRemove_ == REMOVE)
				continue;
			if (itbXY->moveDir_ == RIGHT) {
				count += itbXY->bond_->bondVal[0];
				assert(itbXY->bond_->bondVal[0] >= 0);
				count -= itbXY->bond_->bondVal[1];
				assert(itbXY->bond_->bondVal[1] >= 0);
			} else {
				count -= itbXY->bond_->bondVal[0];
				assert(itbXY->bond_->bondVal[0] >= 0);
				count += itbXY->bond_->bondVal[1];
				assert(itbXY->bond_->bondVal[1] >= 0);
			}
			totalBonds += itbXY->bond_->bondVal[0] + itbXY->bond_->bondVal[1];

		}
#if SIM_ALG==0
		if (sectorType_ == GXY) {
			if (it == this->getIra())
			count--;
			if (it == this->getMasha())
			count++;
		}
#endif
		if (count != 0) {
			return false;
		}
		count = 0;
		Site::IsingBondArray::iterator itbIsing = it->IsingBondArray_.begin();
		for (; itbIsing != it->IsingBondArray_.end(); itbIsing++) {
			if (itbIsing->createRemove_ == REMOVE)
				continue;
			count += itbIsing->bond_->bondVal;
			totalBonds += itbIsing->bond_->bondVal;
		}
#if SIM_ALG==0
		if (sectorType_ == GISING) {
			if (it == this->getIra())
			count--;
			if (it == this->getMasha())
			count++;
		}
#endif
		if ((count % 2) != 0) {
			return false;
		}
	}
	return true;
}
Lattice::XYBondArrayIter Lattice::XYBondBegin() {
	return XYBondLattice_.begin();
}
Lattice::XYBondArrayIter Lattice::XYBondEnd() {
	return XYBondLattice_.end();
}
Lattice::IsingBondArrayIter Lattice::IsingBondBegin() {
	return IsingBondLattice_.begin();
}
Lattice::IsingBondArrayIter Lattice::IsingBondEnd() {
	return IsingBondLattice_.end();
}
Lattice::SiteIter Lattice::siteBegin() {
	return siteLattice_.begin();
}
Lattice::SiteIter Lattice::siteEnd() {
	return siteLattice_.end();
}
Lattice::SiteIterIndex Lattice::siteBeginIndex() {
	return siteLattice_.beginIndex();
}
Lattice::SiteIterIndex Lattice::siteEndIndex() {
	return siteLattice_.endIndex();
}
SectorType Lattice::getSectorType() const {
	return sectorType_;
}
void Lattice::setSectorType(SectorType sectorType) {
	sectorType_ = sectorType;
}
void Lattice::dump(string fileName, bool final,ofstream *outFile) {
	try {
		bool testConf = testConfiguration(totalBonds_);
		if (!testConf)
			throw "Bad Configuration Dumping";
	} catch (string &e) {
		*outFile << e;
		exit(1);
	}
	if (final)
		fileName += "/Latdat.zip";
	else
		fileName += "/LatdatTemp.zip";
	namespace io = boost::iostreams;
	namespace ar = boost::archive;
	ofstream file(fileName.c_str(),
			std::ios::out | std::ios::binary | std::ios::trunc);
	io::filtering_ostream out;
	out.push(io::zlib_compressor());
	out.push(file);
	ar::text_oarchive oa(out);
//	ofstream file(fileName.c_str(), ios_base::out);
//	ar::text_oaromchive oa(file);
	oa << *this;

}
void Lattice::read(string fileName) {
	try {
		fileName += "/Lat.dat.zip";
		namespace io = boost::iostreams;
		namespace ar = boost::archive;
		ifstream file(fileName.c_str(), ios_base::in | ios_base::binary);
		io::filtering_istream in;
		in.push(io::zlib_decompressor());
		in.push(file);
		ar::text_iarchive oi(in);
		oi >> *this;
	} catch (string &s) {
		cout << s;
		exit(1);
	}
}
